#include <stdio.h>
#include <math.h>
#include <string.h>
#include "slave.h"

__thread_local volatile unsigned long get_reply,put_reply;

__thread_local double ex_slave[TZ1][TZ2];
__thread_local double ey_slave[TZ1][TZ2];
__thread_local double hz_slave[TZ1][TZ2];
__thread_local_fix double shade_hz_left[TZ1];//hz[i][j-1]
__thread_local_fix double shade_hz_up[TZ2];//hz[i-1][j]
__thread_local_fix double shade_ex[TZ1];//ex[i][j+1]
__thread_local_fix double shade_ey[TZ2];//ey[i+1][j]
__thread_local_fix int my_task_id,my_id,my_task_num,my_num_x,my_num_y,thread_nums,my_iter;


extern double ex[NX][NY+1];
extern double ey[NX+1][NY];
extern double hz[NX][NY];
//extern int task_num[64];
extern int num_x,num_y,thread_num,iter;

#define min(a,b) ((a>b)?(b):(a))
#define max(a,b) ((a>b)?(a):(b))

#ifdef PROFILING 
//__thread_local_fix volatile unsigned long total_start,total_end;
__thread_local_fix volatile unsigned long dma_get_start,dma_put_start,dma_get_end,dma_put_end;
__thread_local_fix volatile unsigned long comp_start,comp_end;

extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
//extern unsigned long slave_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void func()
{
    int bsize,stride,tsize;
    int i,j,k,ii,jj;
    int dma_get_num,dma_put_num;
    
    my_id=athread_get_id(-1);

    my_num_y=num_y;
    my_num_x=num_x;
    //my_iter=iter;
    thread_nums=thread_num;
    
    if(my_id>=thread_nums)
        my_task_num=0;
    else if(my_id >= ((my_num_x * my_num_y)%thread_nums) )
        my_task_num = (my_num_x * my_num_y) /thread_nums;
    else 
        my_task_num=(my_num_x * my_num_y) /thread_nums + 1;
    
    // my_task_num=task_num[my_id];
    // printf("%d",my_task_num);
    
    for(my_iter=0; my_iter<TMAX; my_iter++){
	
    //loop1
    for (k=0; k<my_task_num; k++)//
    {
        my_task_id=my_id+k*(thread_nums);

        i = my_task_id / my_num_y * TZ1;
        if(i>0)
            break;
        j = my_task_id % my_num_y * TZ2;
        
        //compute
#ifdef PROFILING   
        comp_start=rtc_();        
#endif        
        for(jj=0;jj<min(NY,TZ2);jj++)
            ey_slave[0][jj]=my_iter;
#ifdef PROFILING      
        comp_end=rtc_();
        dma_put_start=rtc_();
#endif
        //write back
        //ey_slave[0,0:TZ2-1]->ey[0,j:j+TZ2-1]
        put_reply=0;
        athread_put(PE_MODE
            , &ey_slave[0][0], &ey[0][j]
            , TZ2*8, (void*)&put_reply
            , 0, 0); 
        while(put_reply!=1);
#ifdef PROFILING    
        dma_put_end=rtc_();
        slave_comp_count[my_id]=slave_comp_count[my_id]+(comp_end-comp_start);
        slave_dma_count[my_id]=slave_dma_count[my_id]+(dma_put_end-dma_put_start);
        dma_put_end=dma_put_start=0;
        comp_end=comp_start=0;
#endif   
    }
   // athread_syn(ARRAY_SCOPE,-1);

    
    //loop2
    for (k=0; k<my_task_num; k++)
    {
        my_task_id=my_id+k*(thread_nums);

        i = my_task_id / my_num_y * TZ1;
        j = my_task_id % my_num_y * TZ2;

        //get data
        //ey[i:i+TZ1-1,j:j+TZ2-1]->ey_slave[0:TZ1-1,0:TZ2-1]
        get_reply=0;
        //get_reply1=0;
        dma_get_num=0;
#ifdef PROFILING    
        //total_start=rtc_();
        dma_get_start=rtc_();
#endif         
        bsize=TZ2*8;
        stride=(NY-TZ2)*8;
        tsize=bsize*TZ1;
        athread_get(PE_MODE
            , &ey[i][j], &ey_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        //hz[i:i+TZ1-1,j:j+TZ2-1]->hz_slave[0:TZ1-1,0:TZ2-1]
        bsize=TZ2*8;
        stride=(NY-TZ2)*8;  
        tsize= bsize*TZ1;
        athread_get(PE_MODE
            , &hz[i][j], &hz_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        dma_get_num+=2;
        if(max(i,1)!=1)
        {
            athread_get(PE_MODE
                ,&hz[i-1][j],&shade_hz_up[0]
                ,TZ2*8,(void*)&get_reply
                ,0,0,0); 
            dma_get_num++;
        }
        while(get_reply!=dma_get_num);

        //compute
#ifdef PROFILING  
        dma_get_end=rtc_();
        comp_start=rtc_();
#endif        
        for(ii=1; ii<TZ1; ii++)
        {
            for(jj=0; jj<TZ2; jj++)
            {
                ey_slave[ii][jj] = ey_slave[ii][jj] - 0.5*(hz_slave[ii][jj] - hz_slave[ii-1][jj]);
            } 
        }
        //while(get_reply1!=dma_get_num);
        if(max(i,1)!=1)
        {
            for(jj=0;jj<TZ2;++jj)
                ey_slave[0][jj] = ey_slave[0][jj] - 0.5*(hz_slave[0][jj] - shade_hz_up[jj]);
        }
#ifdef PROFILING      
        comp_end=rtc_();
        dma_put_start=rtc_();
#endif        
        //printf("compute is over");
        //write back
        //ey_slave[0:TZ1-1,0:TZ2-1]->ey[i:i+TZ1-1,j:j+TZ2-1]
        put_reply=0;
        bsize=TZ2*8;
        stride=(NY-TZ2)*8; 
        tsize= bsize*TZ1;
        athread_put(PE_MODE 
            , &ey_slave[0][0], &ey[i][j]
            , tsize, (void*)&put_reply
            , stride, bsize);
        while(put_reply!=1);
#ifdef PROFILING 
        dma_put_end=rtc_();
        //total_end=rtc_();
        slave_comp_count[my_id]=slave_comp_count[my_id]+(comp_end-comp_start);
        slave_dma_count[my_id]=slave_dma_count[my_id]+(dma_put_end-dma_put_start)+(dma_get_end-dma_get_start);
        comp_end=comp_start=0;
        dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
        //slave_count[my_id]=slave_count[my_id]+(total_end-total_start);
#endif       
        //count_loop2=count_loop2+(end2-start2);
    }
    //athread_syn(ARRAY_SCOPE,-1);
    //slave_count[my_id]+=count_loop2;
    
    //loop3
    for (k=0; k<my_task_num; k++)
    {
        my_task_id=my_id+k*(thread_nums);

        i = my_task_id / my_num_y * TZ1;
        j = my_task_id % my_num_y * TZ2;
        //get data
        //ex[i:i+TZ1-1,j:j+TZ2-1]->ex_slave[0:TZ1-1,0:TZ2-1]
#ifdef PROFILING    
    //total_start=rtc_();
    dma_get_start=rtc_();
#endif
        get_reply=0;
        //get_reply1=0;
        dma_get_num=0;
   
        bsize=TZ2*8;
        stride=(NY+1-TZ2)*8;
        tsize=bsize*TZ1;
        athread_get(PE_MODE
            , &ex[i][j], &ex_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        //hz[i:i+TZ1-1,j:j+TZ2-1]->hz_slave[0:TZ1-1,0:TZ2-1]
        bsize=TZ2*8;
        stride=(NY-TZ2)*8;   
        tsize= bsize*TZ1;
        athread_get(PE_MODE
            , &hz[i][j], &hz_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        dma_get_num+=2;
        if(max(j,1)!=1)
        {
            bsize=8;
            stride=(NY-1)*8;
            tsize=bsize*TZ1;
            athread_get(PE_MODE
                ,&hz[i][j-1],&shade_hz_left[0]
                ,tsize,(void*)&get_reply
                ,0,stride,bsize); 
            dma_get_num++;
            
            /*for(ii=0,jj=i;ii<TZ1;ii++,jj++)
            {
                shade_hz_left[ii]=hz[jj][j-1];
            }*/
        }
       // while(get_reply!=2);//dma_get_num);
        while(get_reply!=dma_get_num);
        //compute
#ifdef PROFILING  
        dma_get_end=rtc_();
        comp_start=rtc_();
#endif        
        for(ii=0; ii<TZ1; ii++)
        {
            for(jj=1; jj<TZ2; jj++)
            {
                ex_slave[ii][jj] = ex_slave[ii][jj] - 0.5*(hz_slave[ii][jj] - hz_slave[ii][jj-1]);
            } 
        }
        //while(get_reply1!=dma_get_num);
        if(max(j,1)!=1)
        {
            for(ii=0;ii<TZ2;ii++)
                ex_slave[ii][0] = ex_slave[ii][0] - 0.5*(hz_slave[ii][0] - shade_hz_left[ii]);
        }
#ifdef PROFILING      
        comp_end=rtc_();
        dma_put_start=rtc_();
#endif         
        //printf("compute is over");
        //write back
        //ey_slave[0:TZ1-1,0:TZ2-1]->ey[i:i+TZ1-1,j:j+TZ2-1]
        dma_put_num=0;
        put_reply=0;
        bsize=TZ2*8;
        stride=(NY+1-TZ2)*8; 
        tsize= bsize*TZ1;
        athread_put(PE_MODE 
            , &ex_slave[0][0], &ex[i][j]
            , tsize, (void*)&put_reply
            , stride, bsize);
        while(put_reply!=1);
#ifdef PROFILING 
        dma_put_end=rtc_();
        //total_end=rtc_();
        slave_comp_count[my_id]=slave_comp_count[my_id]+(comp_end-comp_start);
        slave_dma_count[my_id]=slave_dma_count[my_id]+(dma_put_end-dma_put_start)+(dma_get_end-dma_get_start);
        comp_end=comp_start=dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
        //slave_count[my_id]=slave_count[my_id]+(total_end-total_start);
#endif
    }
   // athread_syn(ARRAY_SCOPE,-1);
    
    //loop4
    for (k=0; k<my_task_num; k++)
    {
        my_task_id=my_id+k*(thread_nums);

        i = my_task_id / my_num_y * TZ1;
        j = my_task_id % my_num_y * TZ2;
        //printf("i=:%d,j=%d\n",i,j);

        //get data
        get_reply=0;
        //get_reply1=0;
        dma_get_num=0;
#ifdef PROFILING  
    //total_start=rtc_();
    dma_get_start=rtc_(); 
#endif        
        //ex[i:i+TZ1-1,j:j+TZ2-1]->ex_slave[0:TZ1-1,0:TZ2-1]
        bsize=TZ2*8;
        stride=(NY+1-TZ2)*8;
        tsize=bsize*TZ1;
        athread_get(PE_MODE
            , &ex[i][j], &ex_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        //ey[i:i+TZ1-1,j:j+TZ2-1]->ey_slave[0:TZ1-1,0:TZ2-1]
        bsize=TZ2*8;
        stride=(NY-TZ2)*8;
        tsize=bsize*TZ1;
        athread_get(PE_MODE
            , &ey[i][j], &ey_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        //hz[i:i+TZ1-1,j:j+TZ2-1]->hz_slave[0:TZ1-1,0:TZ2-1]
        bsize=TZ2*8;
        stride=(NY-TZ2)*8;   
        tsize= bsize*TZ1;
        athread_get(PE_MODE
            , &hz[i][j], &hz_slave[0][0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        //dma_get_num+=2;
        //fetch ex shada data
        bsize=8;
        stride=(NY+1-1)*8;  //stride: from blocktail to blockhead 
        tsize= bsize*(TZ1);
        athread_get(PE_MODE
            ,&ex[i][j+TZ2], &shade_ex[0]
            , tsize, (void*)&get_reply
            , 0, stride, bsize);
        
        /*for(ii=0,jj=i;ii<TZ1;ii++,jj++)
        {
            shade_ex[ii]=ex[jj][j+TZ2];
        } */       
        //fetch ey shade data
        athread_get(PE_MODE
            ,&ey[i+TZ1][j],&shade_ey[0]
            ,TZ2*8,(void*)&get_reply
            ,0,0,0);
        while(get_reply!=5);
        //printf("%d:get id over\n",my_id);
        //compute
#ifdef PROFILING  
        dma_get_end=rtc_();
        comp_start=rtc_();
#endif        
        for(ii=0; ii<TZ1-1; ii++)
            for(jj=0; jj<TZ2-1; jj++)
            {
                hz_slave[ii][jj] = hz_slave[ii][jj]-0.7*(ex_slave[ii][jj+1] \
                             - ex_slave[ii][jj] + ey_slave[ii+1][jj] - ey_slave[ii][jj]);
            }
           
        //while(get_reply1!=2);
        for(jj = 0; jj < TZ2-1; jj++)
        { 
            hz_slave[TZ1-1][jj] = hz_slave[TZ1-1][jj] - 0.7*(ex_slave[TZ1-1][jj+1] \
                                - ex_slave[TZ1-1][jj] + shade_ey[jj] - ey_slave[TZ1-1][jj]); 
        }
          
        for(ii=0; ii<TZ1-1; ii++)
        {
            hz_slave[ii][TZ2-1] = hz_slave[ii][TZ2-1] - 0.7*(shade_ex[ii]\
                               - ex_slave[ii][TZ2-1] + ey_slave[ii+1][TZ2-1] - ey_slave[ii][TZ2-1]);
        }
         
        hz_slave[TZ1-1][TZ2-1] = hz_slave[TZ1-1][TZ2-1] - 0.7*(shade_ex[TZ1-1] \
                                - ex_slave[TZ1-1][TZ2-1]+shade_ey[TZ2-1] - ey_slave[TZ1-1][TZ2-1]);
        
#ifdef PROFILING      
        comp_end=rtc_();
        dma_put_start=rtc_();
#endif        
        //printf("compute is over");
        //write back
        //ey_slave[0:TZ1-1,0:TZ2-1]->ey[i:i+TZ1-1,j:j+TZ2-1]
        //dma_put_num=0;
        put_reply=0;
        bsize=TZ2*8;
        stride=(NY-TZ2)*8; 
        tsize= bsize*TZ1;
        athread_put(PE_MODE 
            , &hz_slave[0][0], &hz[i][j]
            , tsize, (void*)&put_reply
            , stride, bsize);
        while(put_reply!=1);
#ifdef PROFILING 
        dma_put_end=rtc_();
        //total_end=rtc_();
        slave_comp_count[my_id]=slave_comp_count[my_id]+(comp_end-comp_start);
        slave_dma_count[my_id]=slave_dma_count[my_id]+(dma_put_end-dma_put_start)+(dma_get_end-dma_get_start);
        comp_end=comp_start=dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
        //slave_count[my_id]=slave_count[my_id]+(total_end-total_start);
#endif        
    }
    athread_syn(ARRAY_SCOPE,-1);  
}//end of TMAX
}
